package com.quantum.util
{

	import com.quantum.collection.DictionarySet;
	import com.quantum.collection.Set;
	import mx.collections.ArrayCollection;
	import mx.collections.IList;

	public class ArrayUtils
	{
		
		public static function isEmpty(object:Object):Boolean {
			return object == null || (object.hasOwnProperty("length") && object["length"] == 0);
		}
		/**
		 * Returns a filtered version of the given array using the given filter function and
		 * an argument for the filter, if any. An item is included if the filter returns true
		 * for it. The filter function can take the following forms:
		 * <p>   function filter( item:T1 ):Boolean;
		 * <p>   function filter( item:T1, filterArg:T2 ):Boolean;
		 */
		public static function filter(array:Array, filter:Function, filterArg:*=null):Array
		{
			var filterFunc:Function;
			if (filterArg != null)
			{
				filterFunc=function(item:*, index:int, array:Array):Boolean
				{
					return filter(item, filterArg);
				}
			}
			else
			{
				filterFunc=function(item:*, index:int, array:Array):Boolean
				{
					return filter(item);
				}
			}
			var filteredArray:Array=array.filter(filterFunc);
			return filteredArray;
		}


		/**
		 * Returns a filtered version of the given array using the given filter function and
		 * an argument for the filter, if any. An item is excluded if the filter returns true
		 * for it. The filter function can take the following forms:
		 * <p>   function filter( item:T1 ):Boolean;
		 * <p>   function filter( item:T1, filterArg:T2 ):Boolean;
		 */
		public static function filterOut(array:Array, filter:Function, filterArg:*=null):Array
		{
			var filterFunc:Function;
			if (filterArg != null)
			{
				filterFunc=function(item:*, index:int, array:Array):Boolean
				{
					return !filter(item, filterArg);
				}
			}
			else
			{
				filterFunc=function(item:*, index:int, array:Array):Boolean
				{
					return !filter(item);
				}
			}
			var filteredArray:Array=array.filter(filterFunc);
			return filteredArray;
		}


		/**
		 * Returns true if there are any common elements in the given collections.
		 */
		public static function arraysIntersect(arr1:Array /*?*/, arr2:Array /*?*/):Boolean
		{
			var set1:Set /*?*/=asSet(arr1);
			for each (var elem2:*in arr2)
			{
				if (set1.contains(elem2))
				{
					return true;
				}
			}
			return false;
		}

		/**
		 * Removes the duplicate items in the given array.
		 */
		public static function removeDuplicates(objects:Array /*?*/):void
		{
			if (objects == null || objects.length == 0)
			{
				return;
			}
			var objectSet:Set=new DictionarySet();
			var index:int=0;
			while (index < objects.length)
			{
				var item:Object=objects[index];
				if (objectSet.contains(item))
				{
					objects.splice(index, 1);
					continue;
				}
				objectSet.add(item);
				index++;
			}
		}
		
//		http://blog.minidx.com/2008/07/12/1069.html
//		private function removedDuplicates(item:Object, idx:uint, arr:Array):Boolean {
//			if (keys.hasOwnProperty(item.data)) {
//				/* If the keys Object already has this property,
//				return false and discard this item. */
//				return false;
//			} else {
//				/* Else the keys Object does *NOT* already have
//				this key, so add this item to the new data
//				provider. */
//				keys[item.data] = item;
//				return true;
//			}
		

		/**
		 * Append array items to another existing array, thus avoid creating the
		 * unncessary (sometimes) copy that <code>Array.concat</code> method does.
		 *
		 * @param toArray
		 *    The array where to append the items
		 *
		 * @param fromArray
		 *    The array from where to take the items
		 */
		public static function append(toArray:Array, fromArray:Array):void
		{
			if (fromArray == null || fromArray.length == 0)
			{
				return;
			}

			if (toArray == null)
			{
				return;
			}

			for each (var item:*in fromArray)
			{
				toArray.push(item);
			}
		}

		/**
		 * Converts the given object into an Array if it's a well-known collection type, or
		 * else wraps the object in a 1-item array, and returns that array.
		 *
		 * <p>Returns an empty array for a null input arg.
		 */
		public static function objectAsArray(object:*):Array
		{
			if (object == null)
			{
				return [];
			}
			if (object is Array)
			{
				return object;
			}
			if (object is ArrayCollection)
			{
				return ArrayCollection(object).source;
			}
			if (object is IList)
			{
				return IList(object).toArray();
			}
			return [object];
		}

		/**
		 * Removes the given item from the given array.
		 *
		 * @param item
		 *    An item to remove from the array.
		 *
		 * @param array
		 *    The array to search for <code>item</code> that needs to be removed.
		 *
		 * @param comparator
		 *    A function which accepts a couple of objects and returns <code>0</code>
		 *    if they are equal, <code>-1</code> if the first is less than the second
		 *    and <code>1</code> if the first is greater than the second.
		 *    If this parameter is not supplied,
		 *    then the array items are compared with <code>item</code>
		 *    using the <code>==</code> operator.
		 *
		 * @return
		 *    Returns <code>true</code> if the item is found (and therefore removed)
		 *    from the array, otherwise returns <code>false</code>.
		 */
		public static function removeItemFromArray(item:Object, array:Array /*Object*/, comparator:Function=null):Boolean
		{
			if (array == null)
			{
				return false;
			}
			for (var i:int=0; i < array.length; i++)
			{
				var referencedItem:Object=array[i];
				var equal:Boolean=(item == referencedItem);
				if (comparator != null)
				{
					equal=(comparator(item, referencedItem) == 0);
				}
				if (equal)
				{
					// remove the element
					array.splice(i, 1);
					return true;
				}
			}
			return false;
		}

		public static function asSet(array:Array):Set
		{
			var set:Set=new DictionarySet();
			for each (var item:*in array)
			{
				set.add(item);
			}
			return set;
		}


		/**
		 * Gets the array length. If array is null then returns 0.
		 */
		public static function getArrayLength(array:Array):int
		{
			if (array == null)
			{
				return 0;
			}
			return array.length;

		}
	}
}
